package com.soft.zosagent.server;

import com.google.gson.Gson;
import com.ibm.jzos.*;
import com.ibm.os390.security.PlatformReturned;
import com.ibm.os390.security.PlatformUser;
import com.soft.zosagent.common.ACTION;
import com.soft.zosagent.common.Constant;
import com.soft.zosagent.common.OsType;
import com.soft.zosagent.common.UserPwdInfo;
import com.soft.zosagent.pojo.Request;
import com.soft.zosagent.pojo.Response;
import com.soft.zosagent.util.AgentToolUtil;
import com.soft.zosagent.util.ZosUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 
 * @author chenyinbo
 * 
 */
public class ServerHandler extends ChannelInboundHandlerAdapter {

		Logger  logger = Logger.getLogger(ServerHandler.class);
		private UserPwdInfo userPwdInfo = new UserPwdInfo();
	private boolean isUserLogin = false;

	//用来检测当write出现溢满的时候，进行等待



	private Request currentRequest;

	private byte[] LineFeedBytes = System.lineSeparator().getBytes();







	public ServerHandler(){}

	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		super.channelReadComplete(ctx);
	}

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg)
			throws Exception {
		if(msg instanceof Request) {
			Request req = (Request) msg;
			currentRequest = req;
			handleRequest(ctx,req);

		}
	}






	private void handleRequest(ChannelHandlerContext ctx, Request req) throws Exception {
		logger.info("Request Type: [" + req.getAction().toString() + "]");
		logger.info("Request data: [" + (new Gson()).toJson(req)+ "]");
		ACTION code = req.getAction();
		if(code != ACTION.ACTION_USER_LOGIN && !isUserLogin){
			//
			throw new RuntimeException("you are not login.");
		}
		switch (code) {

			case ACTION_USER_LOGIN:{
				String username = req.getUserName();
				String password = req.getUserPwd();
				userPwdInfo.setUser(username);
				userPwdInfo.setPwd(password);
				if(StringUtils.isEmpty(req.getUniqueId())){
					throw new RuntimeException("invalid request. unique ID is empty.");
				}else {

				}
				Response res = new Response();
				res.setOsType(OsType.OS390);
				res.setReq(req);
				res.setResCode(Constant.SuccessCode);
				if(StringUtils.isEmpty(password)){
					res.setResCode(Constant.FAILCode);
					res.setResMessage("password is empty.");
					ctx.channel().writeAndFlush(res).sync();
					ctx.channel().close();
				}else if(StringUtils.isEmpty(username)){
					res.setResCode(Constant.FAILCode);
					res.setResMessage("username is empty.");
					ctx.channel().writeAndFlush(res).sync();
					ctx.channel().close();

				}else{

					if(verifyUserAndPassword(req)){
						//set user login flag;
						isUserLogin = true;
						ctx.channel().writeAndFlush(res).sync();

					}else{
						res.setResCode(Constant.FAILCode);
						res.setResMessage("user and password verify failed.");
						ctx.channel().writeAndFlush(res).sync();
						ctx.channel().close();
					}

				}
				if(isUserLogin){
					logger.info("client user["+req.getUserName()+"] login successfully.");
				}else {
					logger.info("client user["+req.getUserName()+"] login failed.");
				}

			}
			break;


			case ACTION_SENDJOB:{

				//setUserAndPassword(jobJclList);
				Response res = new Response();
				res.setOsType(OsType.OS390);
				res.setReq(req);
				res.setResCode(Constant.SuccessCode);
				try{
					//开始提交JCL
					MvsJobSubmitter mvsJobSubmitter = new MvsJobSubmitter();

					//提交作业
					List<String> jobJclList = null;
					if(AgentToolUtil.checkFileType(req.getFileInfo().getFileName()) == Constant.ZOS_FILE){
						jobJclList = ZosUtil.readPdsMemberFile(req.getFileInfo().getFileName());
					}else {
						jobJclList = req.getJobJclList();
					}
					for (String line : jobJclList) {
						mvsJobSubmitter.write(line);
					}
					mvsJobSubmitter.close();

					String jobId= mvsJobSubmitter.getJobid();
					res.setResMessage(jobId);
					String jobName = getJobnameByJobId(jobId);
					res.setResMessage(String.format("jobname[%s],jobid[%s]",jobName,jobId));

				}catch (Exception e){
					StringWriter sw = new StringWriter();
					e.printStackTrace(new PrintWriter(sw, true));
					res.setResCode(Constant.FAILCode);
					res.setResMessage("submit job failed. msg:"+e.getMessage());
					logger.info("submit job failed. msg:"+sw.toString());
				}
				ctx.channel().writeAndFlush(res).sync();
			}
			break;
			case ACTION_QRYJOB:{
				Response res = new Response();
				res.setOsType(OsType.OS390);
				res.setReq(req);
				res.getJobInfo().setJobId(req.getJobId());
				res.getJobInfo().setJobName(req.getJobName());
				res.setResCode(Constant.SuccessCode);
				try{
					//query job
					String jobStatus = ZosUtil.getStatus(req.getJobName(),req.getJobId());
					ZosUtil.setupJobInfoByJobStatus(res.getJobInfo(),jobStatus);
					res.setResMessage(new Gson().toJson(res.getJobInfo()));
				}catch (Exception e){
					StringWriter sw = new StringWriter();
					e.printStackTrace(new PrintWriter(sw, true));
					res.setResCode(Constant.FAILCode);
					res.setResMessage("query job failed. msg:"+e.getMessage());
					logger.info("query job failed. msg:"+sw.toString());
				}
				ctx.channel().writeAndFlush(res).sync();
			}
			break;
			case ACTION_RECVJOB:{
				Response res = new Response();
				res.setOsType(OsType.OS390);
				res.setReq(req);
				res.setResCode(Constant.SuccessCode);
				try{
					//query job
					res.setResMessage(String.join("\n",ZosUtil.writeJobOutput(req.getJobName(),req.getJobId())));
				}catch (Exception e){
					StringWriter sw = new StringWriter();
					e.printStackTrace(new PrintWriter(sw, true));
					res.setResCode(Constant.FAILCode);
					res.setResMessage("query job failed. msg:"+e.getMessage());
					logger.info("query job failed. msg:"+sw.toString());
				}
				ctx.channel().writeAndFlush(res).sync();
			}
			break;
			case ACTION_CANCELJOB:{
				Response res = new Response();
				res.setOsType(OsType.OS390);
				res.setReq(req);
				res.setResCode(Constant.SuccessCode);
				res.setResMessage(Constant.SuccessMsg);
				try{
					//canel job
					ZosUtil.cancelJob(req.getJobId());
				}catch (Exception e){
					StringWriter sw = new StringWriter();
					e.printStackTrace(new PrintWriter(sw, true));
					res.setResCode(Constant.FAILCode);
					res.setResMessage("cancel job failed. msg:"+e.getMessage());
					logger.info("cancel job failed. msg:"+sw.toString());
				}
				ctx.channel().writeAndFlush(res).sync();
			}break;
			default:
				break;
		}
	}

	private String getJobnameByJobId(String jobId) {

		try {
			String jobName = ZosUtil.getJobNameByJobId(jobId);
			if(StringUtils.isNotBlank(jobName) && jobName.indexOf("(")>0 ){
				return jobName.substring(jobName.indexOf("(")+1,jobName.length()-1);
			}else{
				return jobName;
			}
		}catch (Exception e){
			logger.error("根据joib["+jobId+"] 获取作业名失败.",e);
		}
		return "";

	}

	private void setUserAndPassword(List<String> jobJclList) {
		//为空，直接返回
		if(jobJclList.size()<=0){
			return;
		}

		//没有作业头直接推出
		String firstLine = jobJclList.get(0);
		if(firstLine.contains(" JOB ")){

		}else{
			return;
		}


		//找到第一个step，获取作业头内容
		int linenum=1;
		for(int i=1;i<jobJclList.size();i++){
			if(jobJclList.get(i).matches("//[A-Z]+.*")){
				linenum = i;
				break;
			}
		}

		//查看是否有user关键字
		for(int i=1;i<linenum;i++){
			if(jobJclList.get(i).matches("//\\s+[A-z0-9,=]*USER=.*")){
				return;
			}
		}
		jobJclList.add(2,"// USER="+userPwdInfo.getUser().toUpperCase()+",");
		jobJclList.add(2,"// PASSWORD="+userPwdInfo.getPwd().toUpperCase()+",");

	}

	private boolean verifyUserAndPassword(Request req) throws Exception {
		//todo
		String userName = req.getUserName();
		String password = req.getUserPwd();
		password = decryptPassword(password);
		if(true){

			PlatformReturned platformReturned = PlatformUser.authenticate(userName,password);
			if(platformReturned != null){
			logger.info("1authenticate return info:"+(new Gson()).toJson(platformReturned) );
			}
			if(platformReturned == null || ( platformReturned.success)){
				return true;
			}

		}
		return false;
	}

	private String decryptPassword(String password) {
		return password;
	}

	private boolean isFileExits() throws Exception{

//		if (internalFileInfo.fileType.equals(Constant.ZOS_FILE)) {
//			return ZFile.exists("//'" + internalFileInfo.srvFilename + "'");
//		}else{
//			return (new File(internalFileInfo.srvFilename).exists());
//		}
		return false;
	}






	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
		cause.printStackTrace();
		logger.warn("Exception happens. close channel.",cause);
		ctx.channel().close();// 发生异常，关闭链路
	}

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
	 	ctx.channel().config().setWriteBufferHighWaterMark(1024*1024*1);
		ctx.channel().config().setWriteBufferLowWaterMark(128*1024);
		super.channelActive(ctx);

		//System.out.println((new Gson()).toJson(ctx.pipeline().names()));
	}

	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		super.channelInactive(ctx);
		logger.info("client disconnected.");
	}

	@Override
	public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
		//System.out.println(ctx.channel().isWritable());

		ctx.fireChannelWritabilityChanged();
		//System.out.println(ctx.channel().isWritable());
	}

}
